﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine.Sgml.Merging;
using System.Diagnostics;
using Engine.Sgml.Matching;
using Engine.Sgml.Validation;

namespace Engine.Sgml.Allocation {
    internal sealed class InnerBlocksAllocationStrategy : IAllocationStrategy {
        private ModelMatcher modelMatcher = new ModelMatcher();
        private MatchMerger matchMerger = new MatchMerger();

        public void Allocate(IList<BlockItem> blocks, AllocationContext context) {
            Guard.ArgumentNotNull(blocks, "blocks");
            Guard.ArgumentNotNull(context, "context");
            Debug.Assert(blocks.Count > 0);

            IAllocationStrategy strategy = context.GetStrategy("BlocksAllocation");
            IBlockAllocator blockAllocator = context.GetBlockAllocator("BlockAllocator");

            for (int i = 0; i < blocks.Count; i++) {
                Debug.Assert(blocks[i].InconsistentBlocks.Count > 0);

                SElement ancestor = blocks[i].Ancestor;
                IList<InconsistentBlock> inconsistentBlocks = blocks[i].InconsistentBlocks;
                AllocateInnerOfBlocks(ancestor, inconsistentBlocks);

                while (inconsistentBlocks.Count > 0) {
                    blockAllocator.Allocate(new BlockItem {
                        Ancestor = ancestor,
                        InconsistentBlocks = inconsistentBlocks
                    }, context);
                    if (inconsistentBlocks.Count == 0)
                        break;
                    AllocateInnerOfBlocks(ancestor, inconsistentBlocks);
                    }
                }
            }

        private void AllocateInnerOfBlocks(SElement ancestor, IList<InconsistentBlock> blocks) {
            for (int i = 0; i < blocks.Count; i++) {
                InconsistentBlock block = blocks[i];
                block.Relocate();
                // Removes all inconsistent elements from their ancestor.
                for (int j = 0; j < block.Count; j++) {
                    block[j].Remove();
                    }
                block.Clear();
                }
            blocks.Clear();

            IList<Capture> captures = modelMatcher.Match(ancestor);
            IList<InconsistentBlock> ib = matchMerger.Merge(captures);

            for (int j = 0; j < ib.Count; j++) {
                blocks.Add(ib[j]);
                }
            }
        }
    }
